/*
 * author      : 吴中勤
 * date        : 2019/3/13
 * description : TODO
 */

package org.wzq.learn.infrastructure.lib.trace;

import io.sentry.Sentry;
import io.sentry.SentryClient;
import io.sentry.SentryClientFactory;
import io.sentry.context.Context;
import io.sentry.event.BreadcrumbBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 错误上报发送端, 使用异步方式
 */
public class ErrorReporter {
    private static final Logger logger = LoggerFactory.getLogger(ErrorReporter.class);

    private volatile static ErrorReporter errorReporter; // singleton
    private SentryClient sentry;
    private String serviceName;
    // 发送请求的线程池
    private ThreadPoolExecutor executor = new ThreadPoolExecutor(
            8,
            16,
            600,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(64)
    );

    private ErrorReporter(String serviceName, String sentryDsn) {
        this.serviceName = serviceName;
        this.sentry = SentryClientFactory.sentryClient(sentryDsn);
    }

    public static ErrorReporter init(String serviceName, String sentryDsn) {
        if (null == errorReporter) {
            synchronized (ErrorReporter.class) {
                if (null == errorReporter) {
                    errorReporter = new ErrorReporter(serviceName, sentryDsn);
                }
            }
        }
        return errorReporter;
    }

    public static ErrorReporter getInstance() {
        if (null == errorReporter) {
            throw new RuntimeException("sentry 未初始化, 必须调用 init方法初始化");
        }
        return errorReporter;
    }

    public void sendAPIError(Throwable t) {
        this.send(t, null);
    }

    public void sendAPIError(String errMsg) {
        this.send(null, errMsg);
    }

    private void send(Throwable t, String msg) {
        final String xRequestId = CustomThreadContext.getXRequestId();
        final String xTraceId = CustomThreadContext.getXTraceId();
        try {
            this.executor.execute(() -> sendAPIError(xRequestId, xTraceId, t, msg));
        } catch (Exception e) {
            // 队列满了 忽略
            logger.error("添加上报任务失败", e);
        }
    }


    private void sendAPIError(String xTraceId, String xRequestId, Throwable t, String errMsg) {
        if (logger.isInfoEnabled())
            logger.info("sentry.sendAPIError, xTraceId= {}, xRequestId={}", xTraceId, xRequestId);
        Context context = sentry.getContext();
        context.recordBreadcrumb(new BreadcrumbBuilder().setMessage(serviceName).build());
        Sentry.getContext().addExtra("serviceName", serviceName);
        context.addTag(CustomHTTPHeaderKey.X_TRACE_ID, xTraceId);
        context.addTag(CustomHTTPHeaderKey.X_REQUEST_ID, xRequestId);
        context.addTag("serviceName", serviceName);

        if (t != null)
            sentry.sendException(t);
        else
            sentry.sendMessage(errMsg);
    }
}